//------------------------------------------------------------------------
//
// OpenGL Worm - GLowWorm - Based on the game "Snake" for the Nokia
// mobile phones.
//
//     by CP Prinsloo - April 2003
//
//------------------------------------------------------------------------

#include <windows.h>														// Header File For Windows
#include <gl\gl.h>															// Header File For The OpenGL32 Library
#include <gl\glu.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>

using namespace std;
															// Header File For The GLu32 Library
#include "NeHeGL.h"															// Header File For NeHeGL

#pragma hdrstop

//---------------------------------------------------------------------------
#include "NeHeGL.cpp";
//---------------------------------------------------------------------------
#pragma argsused

#ifndef CDS_FULLSCREEN														// CDS_FULLSCREEN Is Not Defined By Some
#define CDS_FULLSCREEN 4													// Compilers. By Defining It This Way,
#endif																		// We Can Avoid Errors

GL_Window*	g_window;
Keys*		g_keys;
static BOOL g_isProgramLooping;		// Window Creation Loop, For FullScreen/Windowed Toggle																		// Between Fullscreen / Windowed Mode

RECT desktop_window;

// Math
GLfloat pi = 3.141592;
GLfloat radian = pi / 180;
GLfloat epsilon = 0.05;         // minimum collision distance

// Cameras
GLfloat step = 2.0;

// Lights
int GL_LIGHT[8] = {GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, GL_LIGHT3, GL_LIGHT4, GL_LIGHT5, GL_LIGHT6, GL_LIGHT7};    // Represent GL lights in a conveniant array

//Fonts
GLuint	base;			// Base display list for the font set

// General classes
#include "vector.cpp"
#include "vertex.cpp"
#include "quat.cpp"
#include "matrix.cpp"
#include "texture.cpp"
#include "math.cpp"      // added a few new functions
#include "polygon.cpp"

// These are the defines for the flags that are passed into glFogi()
#define GL_FOG_COORDINATE_SOURCE_EXT			0x8450
#define GL_FOG_COORDINATE_EXT	       			0x8451

// Here we extern our function pointer for volumetric fog positioning
typedef void (APIENTRY * PFNGLFOGCOORDFEXTPROC) (GLfloat coord);
PFNGLFOGCOORDFEXTPROC glFogCoordfEXT = NULL;

// This stores the desired depth that we want to fog
extern float g_FogDepth = 150.0;
float fogstart = 0;
float fogend = 250;

int redrawtime = 100; // Update the worm position every 100ms ie 10 fps.

// Object base class and derived classes
#include "object.cpp"
#include "camera.cpp"
#include "light.cpp"

// Texture
enum textures { grass_tex, font_tex, wormhole_tex, pandora_tex,
                life_tex, apple_tex, water_tex, otherview_tex,
                glowworm_tex, cross_tex };
TEXTURE * texture = new TEXTURE[10];

#include "font.cpp"

// Camera
CAMERA * camera = new CAMERA[1];

// Lighting
LIGHT * light = new LIGHT[1];
GLfloat lightColor[3] = {1.0, 1.0, 1.0};

// Worm body
list<VECTOR> worm;
int initnumwormsegments = 50;
int phaseinsegments = 0;
int numwormsegments = initnumwormsegments;
GLfloat wormradius = 0.3;
list <VECTOR>::iterator begin = worm.begin();
list <VECTOR>::iterator end = worm.end();

int initialwormspeed = 2.0;
int wormspeed = initialwormspeed;
VECTOR wormdirection(wormspeed*1, wormspeed*0, wormspeed*0);
VECTOR wormangle(0, pi/4, 0);
float anglestep = 2*pi/24;
//float anglestep = 2*pi/4;     //Classic 90deg turns

#define PRE_ENTERING_NAME 0
#define ENTERING_NAME     1
#define ENTERED_NAME      2
#define RESET_GAME        3

VECTOR apple;
VECTOR hole[2];
VECTOR pandora;
const int numtrees = 3;
VECTOR tree[numtrees];
int hereorthere;                // Which end of the wormhole did we hit
int pandoraframe = 0;
int pandoradirection = 0;
int lifeframe = 0;
int appleframe = 0;
int viewframe = 0;
int crossframe = 0;
GLfloat	holespin;
GLfloat	pandoraspin;
int score;
int lives;
int views;
BOOLEAN newgame;
BOOLEAN wormholeactive;
BOOLEAN pandoraactive;
BOOLEAN otherviewactive;
BOOLEAN drowning;
BOOLEAN pause = FALSE;
BOOLEAN splash = TRUE;
BOOLEAN musicplaying = FALSE;
BOOLEAN highscoretable = PRE_ENTERING_NAME;
float WormHoleStarted;
float TeleportStarted;
float PandoraStarted;
float WormUpdateStarted;
float FloodStarted;
float OtherViewStarted;
float PandoraFrameTimer;
float LifeFrameTimer;
float AppleFrameTimer;
float ViewFrameTimer;
float CrossFrameTimer;
float AirTimer;
float PauseStarted;
float RainSeasonStarted;
float WasteTimeTimer;
float maxwaterlevel = -4.5;
float minwaterlevel = -7.2;
float waterlevel = minwaterlevel;
char name[11] = "";
char oldname[10]="";
char character = 65;
typedef struct {
   int  score;
   char name[10];
} highscore;
highscore highscoretabledata[10];

// Heightmap defines
#define	MAP_SIZE 1024           // Size of our .RAW height map
#define	STEP_SIZE 32            // Width and height of each quad
#define	HEIGHT_RATIO 1.0f       // Ratio that the Y is scaled according to the X and Z

#include "heightmap.cpp"        // Height Map loader and plotter functions

BYTE g_HeightMap[MAP_SIZE*MAP_SIZE];	// Holds the height map data
float scaleValue = 0.03f;		// Scale value for the terrain

float scaler = (scaleValue * HEIGHT_RATIO);
float xzoffset = scaleValue * MAP_SIZE / 2;
float yoffset = scaler * (-300 + 5);


#include "collision.cpp"          // new collision detection routine and helper functions6
#include "timer.cpp";
#include "general.cpp";

BOOL Initialize (GL_Window* window, Keys* keys)								// Any GL Init Code & User Initialiazation Goes Here
{
	g_window	= window;
	g_keys		= keys;

	LoadRawFile("Data/terrain.raw", MAP_SIZE * MAP_SIZE, g_HeightMap);

        randomize();

        SetGLProperties();
        SetGLMaterial();
        SetGLLighting();
        SetWorm();
        SetFog();
        SetGLTexture();
        SetGLCamera();
        ResetApple();
        ResetTrees();
        ResetWormHole();
        ResetPandora();
        score = 0;
        lives = 3;
        views = 5;
        newgame = TRUE;
        BuildFont();
        TimerInit();
        LoadHighScoreTable();
	return TRUE;															// Return TRUE (Initialization Successful)
}

void Deinitialize (void)													// Any User DeInitialization Goes Here
{
        delete[] texture;
        delete[] camera;
        delete[] light;

        KillFont();
}

void Update (DWORD milliseconds)											// Perform Motion Updates Here
{
float time = TimerGetTime();

	if (g_keys->keyDown [VK_ESCAPE] == TRUE)								// Is ESC Being Pressed?
		TerminateApplication (g_window);									// Terminate The Program

        if (g_keys->keyDown [VK_NUMPAD6] == TRUE)
        {
           if (highscoretable == ENTERING_NAME) {
              character++;
           }
           else {
                if ((wormangle.y - anglestep) < 0) {
                        wormangle.y = wormangle.y - anglestep + 2*pi;
                }
                else {
                        wormangle.y -= anglestep;
                }
                UpdateWormVector();
           }
        }

        if (g_keys->keyDown [VK_NUMPAD4] == TRUE)
        {
           if (highscoretable == ENTERING_NAME) {
              character--;
           }
           else {
                if ((wormangle.y + anglestep) > 2*pi) {
                        wormangle.y = wormangle.y + anglestep - 2*pi;
                }
                else {
                        wormangle.y += anglestep;
                }
                UpdateWormVector();
           }
        }

//        if (g_keys->keyDown [VK_NUMPAD8] == TRUE)
//                fogstart += 10;

//        if (g_keys->keyDown [VK_NUMPAD2] == TRUE)
//                fogstart -= 10;

//        if (g_keys->keyDown [VK_NUMPAD7] == TRUE)
//                g_FogDepth += 10;
//waterlevel -= 0.5;

//        if (g_keys->keyDown [VK_NUMPAD1] == TRUE)
//                g_FogDepth -= 10;
//waterlevel += 0.5;

//        if (g_keys->keyDown [VK_NUMPAD9] == TRUE)
//                fogend += 10;

//        if (g_keys->keyDown [VK_NUMPAD3] == TRUE)
//                fogend -= 10;

        if ((g_keys->keyDown [VK_NUMPAD5] == TRUE) &&
            (g_keys->keyUp [VK_NUMPAD5] == FALSE))
        {
             if (highscoretable == ENTERING_NAME) {
                if (strlen(name) < 10) {
                   strcpy(oldname, name);
                   sprintf(name, "%s%c", oldname, character);
                   character = 65;
                }
                else {
                   highscoretable = ENTERED_NAME;
                }
                   g_keys->keyDown [VK_NUMPAD5] = FALSE;
             }
             else if ((views > 0) &&
                 (otherviewactive != TRUE)) {
                PlaySound("Sounds/Otherview.wav", NULL, SND_ASYNC); // Other view sound
                otherviewactive = TRUE;
                SetGLCamera();
                OtherViewStarted = time;
                views--;
             }
                g_keys->keyUp [VK_NUMPAD5] = TRUE;
        }

        if (g_keys->keyDown [VK_NUMPAD5] == TRUE)
               g_keys->keyUp [VK_NUMPAD5] = 0;

//        if (g_keys->keyDown [VK_RIGHT] == TRUE)
//camera[0].Delta_z = -10.0;
//               camera[0].Movement_x += step;

//        if (g_keys->keyDown [VK_LEFT] == TRUE)
//camera[0].Delta_z = +10.0;
//               camera[0].Movement_x -= step;

//        if (g_keys->keyDown [VK_DOWN] == TRUE)
//               camera[0].Movement_z += step;

//        if (g_keys->keyDown [VK_UP] == TRUE)
//               camera[0].Movement_z -= step;

//        if (g_keys->keyDown [VK_PRIOR] == TRUE)
//               camera[0].Movement_y += step;

//        if (g_keys->keyDown [VK_NEXT] == TRUE)
//               camera[0].Movement_y -= step;

        if ((g_keys->keyDown [VK_SPACE] == TRUE) &&
            (g_keys->keyUp [VK_SPACE] == FALSE))
        {
          if (splash == TRUE) {
             splash = FALSE;
             WormHoleStarted = time;
             TeleportStarted = time;
             PandoraStarted = time;
             WormUpdateStarted = time;
             AirTimer = time;
             RainSeasonStarted = time;
          }
          else if (highscoretable == PRE_ENTERING_NAME) {
             if (pause == TRUE) {
                UpdateTimers(time);
                pause = FALSE;
             }
             else {
                pause = TRUE;
                PauseStarted = time;
             }
          }
          else if (highscoretable == ENTERING_NAME) {
                   highscoretable = ENTERED_NAME;
          }
                g_keys->keyUp [VK_SPACE] = TRUE;
        }

        if (g_keys->keyDown [VK_SPACE] == TRUE)
               g_keys->keyUp [VK_SPACE] = 0;

                     if ((pause == FALSE) && (splash==FALSE)) {
                        camera[0].Multiplier = milliseconds/1000.0;
                        light[0].Multiplier = milliseconds/1000.0;

                        if (((time - WormHoleStarted) > 30000) && // Wormhole appears every 30 sec
                            (wormholeactive == FALSE)) {
                            wormholeactive = TRUE;
                            WormHoleStarted = time;
                        }
                        if (((time - WormHoleStarted) > 7500) &&  // Wormhole disappears after 7.5 sec
                            (wormholeactive == TRUE)) {
                            wormholeactive = FALSE;
                            WormHoleStarted = time;
                            ResetWormHole();
                        }

                        if (((time - PandoraStarted) > 15000) && // Pandora appears every 90 sec
                            (pandoraactive == FALSE)) {
                            pandoraactive = TRUE;
                            PandoraStarted = time;
                        }
                        if (((time - PandoraStarted) > 15000) &&  // Pandora disappears after 30 sec
                            (pandoraactive == TRUE)) {
                            pandoraactive = FALSE;
                            PandoraStarted = time;
                            ResetPandora();
                        }

                        if (((time - FloodStarted) > 750) &&  // Flood dries up
                            (waterlevel > minwaterlevel)) {
                            FloodStarted = time;
                            waterlevel-=0.01;
                        }

                        if (((time - OtherViewStarted) > 10000) &&  // Other view lasts 10 seconds
                             (otherviewactive == TRUE)) {
                            PlaySound("Sounds/Otherview.wav", NULL, SND_ASYNC); // Other view sound
                            otherviewactive = FALSE;
                            SetGLCamera();
                        }

                        if ((time - RainSeasonStarted) > 90000) { // Every 90 seconds
                            if (waterlevel < maxwaterlevel+1.0)
                               waterlevel+=1.0;
                            else
                               waterlevel=maxwaterlevel;
                            PlaySound("Sounds/Thunder.wav", NULL, SND_ASYNC);
                            RainSeasonStarted = time;
                        }

                        while ((TimerGetTime() - WasteTimeTimer) < 100) {
                        }
                        WasteTimeTimer = TimerGetTime();
                     }
}

void Draw () //DWORD milliseconds)
{
    int time = TimerGetTime();

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();														// Reset The Modelview Matrix

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);					// Clear Screen And Depth Buffer

    if (splash == TRUE) {
    camera[0].Apply();
    DrawSplash();
    DrawCross();
       if (musicplaying == FALSE) {
          PlaySound("Sounds/Thunder.wav", NULL, SND_ASYNC);  // GlowWorm.mid
          musicplaying = TRUE;
       }
       glPrint(140, 120, 1, "by C P Prinsloo");
       glPrint( 80,  80, 1, "A game based on Nokia Snake");
       glPrint(520,  10, 1, "John 1:5");
    DrawHighScoreTable();
    return;
    }

//    SetFog();

    camera[0].Update();

    if (pause == FALSE) {
          UpdateWormPosition();
    }

    camera[0].Apply();
    DrawLightSphere();

    DrawWorm();
    DrawTrees();

    glScalef(scaleValue, scaleValue * HEIGHT_RATIO, scaleValue);
    RenderHeightMap(g_HeightMap);    		// Render the height map
    glScalef(1/scaleValue, 1/(scaleValue * HEIGHT_RATIO), 1/scaleValue);

    if (waterlevel > minwaterlevel)
       DrawFlood();

    if (wormholeactive == TRUE) DrawWormHole();
    if (pandoraactive == TRUE)  DrawPandora();

    DrawApple();

    //    Check for collision with self
    if (CheckForCollisionWithWorm(light[0].Position, TRUE) == TRUE) {
       PlaySound("Sounds/Ouch.wav", NULL, SND_ASYNC);
       KillWorm(time);
    }

    //    Check for collision with trees
    if (CheckForCollisionWithTrees(light[0].Position) == TRUE) {
       PlaySound("Sounds/Ouch.wav", NULL, SND_ASYNC);
       KillWorm(time);
    }

    //    Check for collision with apple
    if (CheckForCollisionWithApple() == TRUE) {
       PlaySound("Sounds/Chomp.wav", NULL, SND_ASYNC);
       ResetApple();
       score++;
       phaseinsegments+=10;
    }

    if (((time - TeleportStarted) > 1000) &&
        (wormholeactive == TRUE)) {
       hereorthere = CheckForCollisionWithWormHole();
       if (hereorthere > -1) {
          PlaySound("Sounds/Wormhole.wav", NULL, SND_ASYNC);
          WormHoleStarted = time;
          TeleportStarted = time;
          TeleportWorm(hereorthere);
       }
    }

    if (pandoraactive == TRUE) {
       if (CheckForCollisionWithPandora() == TRUE) {
          PandoraStarted = time;
          ResetPandora();
          OpenPandora();
          pandoraactive = FALSE;
       }
    }
      //    Check for worm falling off playing field
    if (light[0].Position.y < -8) {
       PlaySound("Sounds/Ouch.wav", NULL, SND_ASYNC);
       KillWorm(time);
    }

      //    Check for worm drowning
    if (light[0].Position.y < waterlevel) {
       if (drowning == FALSE) {
          PlaySound("Sounds/Bubble.wav", NULL, SND_ASYNC);
          drowning = TRUE;
          AirTimer = time;
       }
       else {
          DrawAir();
          if (pause == FALSE) {
             if ((time - AirTimer) > 3500) {
                PlaySound("Sounds/Ouch.wav", NULL, SND_ASYNC);
                KillWorm(time);
             }
          }
       }
    }
    else {
       if (drowning == TRUE) {
          PlaySound("Sounds/Gasp.wav", NULL, SND_ASYNC);
          drowning = FALSE;
       }
    }

    if (lives == 0) {
       switch (highscoretable) {
          case PRE_ENTERING_NAME:
             if (score > highscoretabledata[9].score) {
                pause = TRUE;
                PauseStarted = time;
                highscoretable = ENTERING_NAME;
             }
             else {
                highscoretable = RESET_GAME;
             }
            break;
          case ENTERING_NAME:
             glPrint(250, 260, 1, "Score: %2d", score);
             glPrint(260, 240, 1, "Name: %s%c", name, character);
            break;
          case ENTERED_NAME:
             EnterNameInTable();
             strcpy(name, "");
             /* FALL_THROUGH */
          case RESET_GAME:
             score = 0;
             lives = 3;
             views = 5;
             newgame = TRUE;
             pause = FALSE;
             splash = TRUE;
             highscoretable = PRE_ENTERING_NAME;
             ResetWorm();
             ResetApple();
             ResetTrees();
             ResetWormHole();
            break;
       }
    }

    glPrint(520, 450, 1, "Score: %2d", score);

    DrawLives();
    DrawViews();

    if ((pause == TRUE) &&
        (highscoretable != ENTERING_NAME)) {
       glPrint(320, 240, 0, "Paused");
    }

//    if (milliseconds != 0)
//    glPrint(10, 20, 1, "Frame Rate: %d", (int)(1000.0/(float)(milliseconds)));
//    glPrint(20, 450, 1, "Lives: %2d", lives);
//    glPrint(10, 30, 1, "Height: %3.2f", light[0].Position.y);
//    glPrint(10, 30, 1, "FogDepth: %3.2f  (%3.2f)", g_FogDepth, g_FogDepth*scaler);
//    glPrint(10, 20, 1, "FogEnd: %3.2f", fogend);
//    glPrint(10, 10, 1, "FogStart: %3.2f", fogstart);
//    glPrint(10, 10, 1, "WaterLevel: %3.2f", waterlevel);

	glFlush ();																// Flush The GL Rendering Pipeline
}
LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	// Get The Window Context
	GL_Window* window = (GL_Window*)(GetWindowLong (hWnd, GWL_USERDATA));

	switch (uMsg)														// Evaluate Window Message
	{
		case WM_SYSCOMMAND:												// Intercept System Commands
		{
			switch (wParam)												// Check System Calls
			{
				case SC_SCREENSAVE:										// Screensaver Trying To Start?
				case SC_MONITORPOWER:									// Monitor Trying To Enter Powersave?
				return 0;												// Prevent From Happening
			}
			break;														// Exit
		}
		return 0;														// Return

		case WM_CREATE:													// Window Creation
		{
			CREATESTRUCT* creation = (CREATESTRUCT*)(lParam);			// Store Window Structure Pointer
			window = (GL_Window*)(creation->lpCreateParams);
			SetWindowLong (hWnd, GWL_USERDATA, (LONG)(window));
		}
		return 0;														// Return

		case WM_CLOSE:													// Closing The Window
			TerminateApplication(window);								// Terminate The Application
		return 0;														// Return

		case WM_SIZE:													// Size Action Has Taken Place
			switch (wParam)												// Evaluate Size Action
			{
				case SIZE_MINIMIZED:									// Was Window Minimized?
					window->isVisible = FALSE;							// Set isVisible To False
				return 0;												// Return

				case SIZE_MAXIMIZED:									// Was Window Maximized?
					window->isVisible = TRUE;							// Set isVisible To True
					ReshapeGL (LOWORD (lParam), HIWORD (lParam));		// Reshape Window - LoWord=Width, HiWord=Height
				return 0;												// Return

				case SIZE_RESTORED:										// Was Window Restored?
					window->isVisible = TRUE;							// Set isVisible To True
					ReshapeGL (LOWORD (lParam), HIWORD (lParam));		// Reshape Window - LoWord=Width, HiWord=Height
				return 0;												// Return
			}
		break;															// Break

		case WM_KEYDOWN:												// Update Keyboard Buffers For Keys Pressed
			if ((wParam >= 0) && (wParam <= 255))						// Is Key (wParam) In A Valid Range?
			{
				window->keys->keyDown [wParam] = TRUE;					// Set The Selected Key (wParam) To True
				return 0;												// Return
			}
		break;															// Break

		case WM_KEYUP:													// Update Keyboard Buffers For Keys Released
			if ((wParam >= 0) && (wParam <= 255))						// Is Key (wParam) In A Valid Range?
			{
				window->keys->keyDown [wParam] = FALSE;					// Set The Selected Key (wParam) To False
				return 0;												// Return
			}
		break;

                case WM_MOUSEMOVE:
//                    camera[0].Delta_x = GLfloat(HIWORD(lParam) - desktop_window.bottom * 0.5); // * 10;
//                    camera[0].Delta_y = GLfloat(LOWORD(lParam) - desktop_window.right * 0.5); // * 10;
                break;                														// Break

		case WM_TOGGLEFULLSCREEN:										// Toggle FullScreen Mode On/Off
			g_createFullScreen = (g_createFullScreen == TRUE) ? FALSE : TRUE;
			PostMessage (hWnd, WM_QUIT, 0, 0);
		break;															// Break
	}

	return DefWindowProc (hWnd, uMsg, wParam, lParam);					// Pass Unhandled Messages To DefWindowProc
}

BOOL RegisterWindowClass (Application* application)						// Register A Window Class For This Application.
{																		// TRUE If Successful
	// Register A Window Class
	WNDCLASSEX windowClass;												// Window Class
	ZeroMemory (&windowClass, sizeof (WNDCLASSEX));						// Make Sure Memory Is Cleared
	windowClass.cbSize   		= sizeof (WNDCLASSEX);					// Size Of The windowClass Structure
	windowClass.style    		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraws The Window For Any Movement / Resizing
	windowClass.lpfnWndProc		= (WNDPROC)(WindowProc);				// WindowProc Handles Messages
	windowClass.hInstance		= application->hInstance;				// Set The Instance
	windowClass.hbrBackground	= (HBRUSH)(COLOR_APPWORKSPACE);			// Class Background Brush Color
	windowClass.hCursor	  	= LoadCursor(NULL, IDC_ARROW);			// Load The Arrow Pointer
	windowClass.lpszClassName	= application->className;				// Sets The Applications Classname
	if (RegisterClassEx (&windowClass) == 0)							// Did Registering The Class Fail?
	{
		// NOTE: Failure, Should Never Happen
		MessageBox (HWND_DESKTOP, "RegisterClassEx Failed!", "Error", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;													// Return False (Failure)
	}
	return TRUE;														// Return True (Success)
}

// Program Entry (WinMain)
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	Application			application;									// Application Structure
	GL_Window			window;											// Window Structure
	Keys				keys;											// Key Structure
	BOOL				isMessagePumpActive;							// Message Pump Active?
	MSG				msg;											// Window Message Structure
	DWORD				tickCount;										// Used For The Tick Counter

	// Fill Out Application Data
	application.className = "OpenGL";									// Application Class Name
	application.hInstance = hInstance;									// Application Instance

	// Fill Out Window
	ZeroMemory (&window, sizeof (GL_Window));							// Make Sure Memory Is Zeroed
	window.keys	    		= &keys;								// Window Key Structure
	window.init.application		= &application;							// Window Application
	window.init.title		= "GLowWorm";	// Window Title
	window.init.width		= 1024; //800; //640;									// Window Width
	window.init.height		= 768;  //600; //480;									// Window Height
	window.init.bitsPerPixel	= 16;									// Bits Per Pixel
	window.init.isFullScreen	= TRUE;									// Fullscreen? (Set To TRUE)

	ZeroMemory (&keys, sizeof (Keys));									// Zero keys Structure

	// Ask The User If They Want To Start In FullScreen Mode?
	if (MessageBox (HWND_DESKTOP, "Would You Like To Run In Fullscreen Mode?", "Start FullScreen?", MB_YESNO | MB_ICONQUESTION) == IDNO)
	{
		window.init.isFullScreen = FALSE;								// If Not, Run In Windowed Mode
	}

	// Register A Class For Our Window To Use
	if (RegisterWindowClass (&application) == FALSE)					// Did Registering A Class Fail?
	{
		// Failure
		MessageBox (HWND_DESKTOP, "Error Registering Window Class!", "Error", MB_OK | MB_ICONEXCLAMATION);
		return -1;							   		// Terminate Application
	}

	g_isProgramLooping = TRUE;						   		// Program Looping Is Set To TRUE
	g_createFullScreen = window.init.isFullScreen;						// g_createFullScreen Is Set To User Default
	while (g_isProgramLooping)						    		// Loop Until WM_QUIT Is Received
	{
        	// Create A Window
		window.init.isFullScreen = g_createFullScreen;					// Set Init Param Of Window Creation To Fullscreen?
		if (CreateWindowGL (&window) == TRUE)					     	// Was Window Creation Successful?
		{
			// At This Point We Should Have A Window That Is Setup To Render OpenGL
			if (Initialize (&window, &keys) == FALSE)			     	// Call User Intialization
			{
				// Failure
				TerminateApplication (&window);				     	// Close Window, This Will Handle The Shutdown
			}
			else								     	// Otherwise (Start The Message Pump)
			{	// Initialize was a success
				isMessagePumpActive = TRUE;				     	// Set isMessagePumpActive To TRUE
				while (isMessagePumpActive == TRUE)			     	// While The Message Pump Is Active
				{
					// Success Creating Window.  Check For Window Messages
					if (PeekMessage (&msg, window.hWnd, 0, 0, PM_REMOVE) != 0)
					{
						// Check For WM_QUIT Message
						if (msg.message != WM_QUIT)		     	// Is The Message A WM_QUIT Message?
						{
							DispatchMessage (&msg);		     	// If Not, Dispatch The Message
						}
						else					     	// Otherwise (If Message Is WM_QUIT)
						{
							isMessagePumpActive = FALSE;	     	// Terminate The Message Pump
						}
					}
					else						     	// If There Are No Messages
					{
						if (window.isVisible == FALSE)		     	// If Window Is Not Visible
						{
							WaitMessage ();			     	// Application Is Minimized Wait For A Message
						}
						else					     	// If Window Is Visible
						{
                                                float TimeElapsed;
							// Process Application Loop
							tickCount = GetTickCount ();	     	// Get The Tick Count
                                                        if (newgame) {
                                                           window.lastTickCount = tickCount;
                                                           ZeroMemory (&keys, sizeof (Keys));
                                                           newgame = FALSE;
                                                        }
                                                        TimeElapsed = tickCount - window.lastTickCount;
							Update (TimeElapsed);                   // Update The Counter
							window.lastTickCount = tickCount;    	// Set Last Count To Current Count
							Draw ();                                // Draw Our Scene

							SwapBuffers (window.hDC);		// Swap Buffers (Double Buffering)
						}
					}
				}														// Loop While isMessagePumpActive == TRUE
			}															// If (Initialize (...

			// Application Is Finished
			Deinitialize ();											// User Defined DeInitialization

			DestroyWindowGL (&window);									// Destroy The Active Window
		}
		else															// If Window Creation Failed
		{
			// Error Creating Window
			MessageBox (HWND_DESKTOP, "Error Creating OpenGL Window", "Error", MB_OK | MB_ICONEXCLAMATION);
			g_isProgramLooping = FALSE;									// Terminate The Loop
		}
	}																	// While (isProgramLooping)

	UnregisterClass (application.className, application.hInstance);		// UnRegister Window Class
	return 0;
}																		// End Of WinMain()

void TerminateApplication (GL_Window* window)							// Terminate The Application
{
        SaveHighScoreTable();

	PostMessage (window->hWnd, WM_QUIT, 0, 0);							// Send A WM_QUIT Message
	g_isProgramLooping = FALSE;											// Stop Looping Of The Program
}


